﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RayDen.Library.Components.Color.Types;
using RayDen.Library.Components.Surface.Spectral;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Data.Imaging;
using RayDen.Library.Entity.Scene;
using RayDen.RayEngine.Corrector.Managers;
using RayDen.RayEngine.Data;
using RayDen.RayEngine.Samplers;
using RayDen.RayEngine.Scene;

namespace RayDen.RayEngine
{
    public class PrimitiveCache : IEnumerable<IScenePrimitive>
    {
        protected List<IScenePrimitive> primitives;
        protected List<SolidObject> solids;
        protected List<LightObject> lights;
        protected List<VolumeObject> volumes;


        public PrimitiveCache(RayEngineScene scene)
        {
            primitives = new List<IScenePrimitive>(scene.Meshes.Length + scene.Lights.Length + 1);
            solids = new List<SolidObject>();
            lights = new List<LightObject>();
            volumes = new List<VolumeObject>();

            foreach (var triangleMesh in scene.Meshes)
            {
                var lightMesh = false;
                if (VolumeSampler.IsVolume(triangleMesh))
                {

                    var vm = scene.VolumeMats.FirstOrDefault(
                        item =>
                            item.Name.Equals(triangleMesh.MaterialName, StringComparison.InvariantCultureIgnoreCase));
                    var vmat = new VolumeMaterialInfo<IColorType>();
                    if (vm != null)
                    {
                        vmat.Absorbtion = ColorManager.Instance.Convert(ref vm.Absorbtion);
                        vmat.Inscattering = ColorManager.Instance.Convert(ref vm.Inscattering);
                        vmat.Outscattering = ColorManager.Instance.Convert(ref vm.Outscattering);
                        vmat.Name = vm.Name;
                        vmat.Albedo = vm.Albedo;
                        vmat.Density = vm.Density;
                    }

                    var volume = new VolumeObject((TriangleMeshInfo)triangleMesh, vmat);
                    primitives.Add(volume);
                    volumes.Add(volume);
                    continue;
                }

                if (LightSampler.IsLight(triangleMesh.MeshName))
                {
                    var lt = scene.Lights.FirstOrDefault(
                        l => l.Name.Equals(triangleMesh.MeshName, StringComparison.InvariantCultureIgnoreCase));
                    if (lt == null)
                    {
                        throw new ArgumentException("Light not found", triangleMesh.MeshName);
                    }
                    var light = new LightObject(lt, lt.Profile);
                    primitives.Add(light);
                    lights.Add(light);
                    lightMesh = true;
                }

                var solid = new SolidObject((TriangleMeshInfo)triangleMesh, scene.MatLib.GetSurfMat(triangleMesh.MaterialName))
                {
                    SurfaceMaterialInfo = new MaterialInfoData<IColorType>(scene.MaterialProvider.Get(triangleMesh.MaterialName), spectrum => ColorManager.Instance.Convert(ref spectrum))
                };
                if (!lightMesh)
                    primitives.Add(solid);
                solids.Add(solid);
            }

            int spectralMode = 0;
            int entityId = 0;
            if (spectralMode == 1)
            {
                scene.spdCache = new SpectralDataCache<SpectralDistribution>();
                var spds = new Dictionary<string, int>();
                foreach (var primMat in scene.MaterialProvider.GetAll())
                {
                    if (!spds.ContainsKey(primMat.Name.ToLower()))
                    {
                        scene.spdCache.AddItem(entityId,
                            ColorFactory.CreateRegularSpd(primMat.Kd.c1,
                                                          primMat.Kd.c2,
                                                          primMat.Kd.c3,
                                                          SpectrumType.Reflectance));
                        scene.spdCache.AddItem(entityId,
                            ColorFactory.CreateRegularSpd(primMat.Ks.c1,
                                                          primMat.Ks.c2,
                                                          primMat.Ks.c3,
                                                          SpectrumType.Reflectance));
                        scene.spdCache.AddItem(entityId,
                            ColorFactory.CreateRegularSpd(primMat.Kr.c1,
                                                          primMat.Kr.c2,
                                                          primMat.Kr.c3,
                                                          SpectrumType.Reflectance));
                        scene.spdCache.AddItem(entityId,
                            ColorFactory.CreateRegularSpd(primMat.Kt.c1,
                                                          primMat.Kt.c2,
                                                          primMat.Kt.c3,
                                                          SpectrumType.Reflectance));
                        scene.spdCache.AddItem(entityId,
                            ColorFactory.CreateRegularSpd(primMat.Ke.c1,
                                                          primMat.Ke.c2,
                                                          primMat.Ke.c3,
                                                          SpectrumType.Illuminant));

                        spds.Add(primMat.Name.ToLower(), entityId);
                        entityId = scene.spdCache.AddEntity();

                    }
                }



            }

        }

        public IScenePrimitive GetPrimitive(uint triangleIndex)
        {
            return primitives.FirstOrDefault(scenePrimitive => scenePrimitive.OwnsIndex(triangleIndex));
        }

        public IEnumerator<IScenePrimitive> GetEnumerator()
        {
            return primitives.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return primitives.GetEnumerator();
        }
    }
}
